#include "t_avcodec.h"
extern "C"
{
#include "libswscale/swscale.h"
}

T_AVFrame::T_AVFrame(AVCodecContext *a_codec_ctx)
    : m_codec_ctx(a_codec_ctx)
{
    m_frame = avcodec_alloc_frame();
}
T_AVFrame::~T_AVFrame()
{
    av_free(m_frame);
}
AVCodecContext *T_AVFrame::codecContext()
{
    return m_codec_ctx;
}
AVFrame *T_AVFrame::frame()
{
    return m_frame;
}
enum PixelFormat T_AVFrame::pix_fmt()
{
    return m_codec_ctx->pix_fmt;
}
int T_AVFrame::width()
{
    return m_codec_ctx->width;
}
int T_AVFrame::height()
{
    return m_codec_ctx->height;
}
uint8_t **T_AVFrame::data()
{
    return m_frame->data;
}
int *T_AVFrame::linesize()
{
    return m_frame->linesize;
}




T_AVPicture_Allocator::T_AVPicture_Allocator()
    : m_pix_fmt(PIX_FMT_NONE)
    , m_width(0)
    , m_height(0)
{
}
T_AVPicture_Allocator::~T_AVPicture_Allocator()
{
}
enum PixelFormat &T_AVPicture_Allocator::f_pix_fmt() { return m_pix_fmt; }
int  &T_AVPicture_Allocator::f_width() { return m_width; }
int  &T_AVPicture_Allocator::f_height() { return m_height; }
AVPicture &T_AVPicture_Allocator::f_avpicture() { return m_avpicture; }
void T_AVPicture_Allocator::reallocate(PixelFormat a_pix_fmt, int a_width, int a_height)
{
    Q_ASSERT(a_pix_fmt!=PIX_FMT_NONE);Q_ASSERT(a_width>0);Q_ASSERT(a_height>0);
    if(m_pix_fmt==a_pix_fmt && m_width==a_width && m_height==a_height) return;
    m_pix_fmt = a_pix_fmt;
    m_width = a_width;
    m_height = a_height;
    int v_num_bytes=::avpicture_get_size(
            m_pix_fmt,
            m_width,
            m_height
            );
    if(this->buffer_size() < (size_t)v_num_bytes)
    {
        this->buffer_resize(v_num_bytes);
    }
    this->p_refill(this->buffer_top());
}
void T_AVPicture_Allocator::p_refill(uchar *a_top)
{
    ::avpicture_fill(
            &m_avpicture,
            a_top, //this->top(),
            this->m_pix_fmt,
            this->m_width,
            this->m_height
            );
}




class T_AVPicture_BasePrivate : public QObject
{
protected:
    T_DECLARE_PUBLIC(T_AVPicture_Base)
protected:
    T_AVPicture_Allocator *m_allocator;
public:
    explicit T_AVPicture_BasePrivate(T_AVPicture_Base *me, T_AVPicture_Allocator *a_allocator)
        : T_INIT_QPTR(me)
        , m_allocator(a_allocator)
    {
    }
    ~T_AVPicture_BasePrivate()
    {
    }
};
T_AVPicture_Base::T_AVPicture_Base(T_AVPicture_Allocator *a_allocator)
{
    T_INIT_DPTR(T_AVPicture_Base)(this, a_allocator);
}
T_AVPicture_Base::~T_AVPicture_Base()
{
}
enum PixelFormat T_AVPicture_Base::pix_fmt()
{
    Q_D(T_AVPicture_Base);
    return d->m_allocator->f_pix_fmt();
}
int T_AVPicture_Base::width()
{
    Q_D(T_AVPicture_Base);
    return d->m_allocator->f_width();
}
int T_AVPicture_Base::height()
{
    Q_D(T_AVPicture_Base);
    return d->m_allocator->f_height();
}
uint8_t **T_AVPicture_Base::data()
{
    Q_D(T_AVPicture_Base);
    return d->m_allocator->f_avpicture().data;
}
int *T_AVPicture_Base::linesize()
{
    Q_D(T_AVPicture_Base);
    return d->m_allocator->f_avpicture().linesize;
}
void T_AVPicture_Base::resize(PixelFormat a_pix_fmt, int a_width, int a_height)
{
    Q_D(T_AVPicture_Base);
    d->m_allocator->reallocate(a_pix_fmt, a_width, a_height);
}
void T_AVPicture_Base::copyFrom(T_AVImage *a_image)
{
    Q_D(T_AVPicture_Base);
    this->resize(a_image->pix_fmt(),
                 a_image->width(),
                 a_image->height());
    AVPicture v_image_pict;
    v_image_pict.data[0] = a_image->data()[0];
    v_image_pict.data[1] = a_image->data()[1];
    v_image_pict.data[2] = a_image->data()[2];
    v_image_pict.linesize[0] = a_image->linesize()[0];
    v_image_pict.linesize[1] = a_image->linesize()[1];
    v_image_pict.linesize[2] = a_image->linesize()[2];
    av_picture_copy(&d->m_allocator->f_avpicture(),
                    &v_image_pict,
                    a_image->pix_fmt(),
                    a_image->width(),
                    a_image->height());
}
void T_AVPicture_Base::scaleFrom(T_AVImage *a_src, int a_sws_flags)
{
    SwsContext *v_sws_ctx = sws_getContext(
            a_src->width(), //int srcW,
            a_src->height(), //int srcH,
            a_src->pix_fmt(), //enum PixelFormat srcFormat,
            this->width(), //int dstW,
            this->height(), //int dstH,
            this->pix_fmt(), //enum PixelFormat dstFormat,
            a_sws_flags, //int flags,
            NULL, //SwsFilter *srcFilter,
            NULL, //SwsFilter *dstFilter,
            NULL  //const double *param
            );
    int v_scale_result = sws_scale(
            v_sws_ctx, //struct SwsContext *context,
            a_src->data(), //const uint8_t* const srcSlice[],
            a_src->linesize(), //const int srcStride[],
            0, //int srcSliceY,
            a_src->height(), //int srcSliceH,
            this->data(), //uint8_t* const dst[],
            this->linesize() //const int dstStride[]
            );
    Q_UNUSED(v_scale_result);
    sws_freeContext(v_sws_ctx);
}
void T_AVPicture_Base::resizeForQImage(int a_depth, int a_width, int a_height)
{
    Q_ASSERT(a_depth==16||a_depth==32);
    switch(a_depth)
    {
    case 16:
        this->resize(PIX_FMT_RGB565, a_width, a_height);
        break;
    case 32:
        this->resize(PIX_FMT_BGRA, a_width, a_height);
        break;
    default:
        Q_ASSERT(0);
    }
}
QImage T_AVPicture_Base::toQImage()
{
    switch(this->pix_fmt())
    {
    case PIX_FMT_RGB565:
        return QImage(this->data()[0], this->width(), this->height(), QImage::Format_RGB16);
        break;
    case PIX_FMT_BGRA:
        return QImage(this->data()[0], this->width(), this->height(), QImage::Format_RGB32);
        break;
    default:
        return QImage();
    }
}




#if 0x0
class T_AVPicturePrivate : public QObject
{
protected:
    T_DECLARE_PUBLIC(T_AVPicture)
protected:
public:
    explicit T_AVPicturePrivate(T_AVPicture *me)
        : T_INIT_QPTR(me)
    {
    }
    ~T_AVPicturePrivate()
    {
    }
};
#endif
T_AVPicture::T_AVPicture(PixelFormat a_pix_fmt, int a_width, int a_height)
    : T_AVPicture_Base(this)
{
    //T_INIT_DPTR(T_AVPicture)(this);
    this->resize(a_pix_fmt, a_width, a_height);
}
T_AVPicture::T_AVPicture(T_AVImage *a_image)
    : T_AVPicture_Base(this)
{
    //T_INIT_DPTR(T_AVPicture)(this);
    this->resize(a_image->pix_fmt(), a_image->width(), a_image->height());
    this->copyFrom(a_image);
}
T_AVPicture::~T_AVPicture()
{
}




T_AVPicture_Allocator_MMF::T_AVPicture_Allocator_MMF(QFile *a_file)
    : m_file(a_file)
    , m_mapped(NULL)
{
    Q_ASSERT(m_file);
    Q_ASSERT(m_file->isOpen());
    Q_ASSERT(m_file->isReadable());
    Q_ASSERT(m_file->isWritable());
}
T_AVPicture_Allocator_MMF::~T_AVPicture_Allocator_MMF()
{
    p_unmap();
}
void T_AVPicture_Allocator_MMF::buffer_resize(size_t a_size)
{
    Q_ASSERT(m_counter>0);Q_ASSERT(a_size>0);
    p_unmap();
    m_file->resize(a_size);
}
size_t T_AVPicture_Allocator_MMF::buffer_size()
{
    Q_ASSERT(m_counter>0);
    return m_file->size();
}
uchar *T_AVPicture_Allocator_MMF::buffer_top()
{
    Q_ASSERT(m_counter>0);
    this->p_remap();
    return m_mapped;
}
void T_AVPicture_Allocator_MMF::attach()
{
    T_ATOMIC_LOCKER(v_locker, &m_counter);
    Q_ASSERT(m_counter>=0);
    m_counter++;
    this->p_remap();
}
void T_AVPicture_Allocator_MMF::detach()
{
    T_ATOMIC_LOCKER(v_locker, &m_counter);
    m_counter--;
    Q_ASSERT(m_counter>=0);
    if(m_counter==0)
    {
        //qDebug() << "[T_AVPicture_Allocator_MMF::detach()] before p_unmap()";
        p_unmap();
    }
}
void T_AVPicture_Allocator_MMF::p_remap()
{
    if(m_mapped) return;
    if(m_pix_fmt == PIX_FMT_NONE) return;
    if(m_file->size()==0) return;
    m_mapped = m_file->map(0, m_file->size());
    Q_ASSERT(m_mapped);
    Q_ASSERT(((uint)m_mapped)%32==0);
    this->p_refill(m_mapped);
}
void T_AVPicture_Allocator_MMF::p_unmap()
{
    if(m_mapped)
    {
        bool b = m_file->unmap(m_mapped);
        Q_ASSERT(b);
        m_mapped = NULL;
    }
}




T_AVRational::T_AVRational(AVRational &a_rational)
    : num(a_rational.num)
    , den(a_rational.den)
{
}
T_AVRational T_AVRational::operator*(qint64 a) const
{
    T_AVRational v_result = *this;
    v_result.num *= a;
    return v_result;
}
T_AVRational& T_AVRational::operator*=(qint64 a)
{
    this->num *= a;
    return *this;
}
T_AVRational T_AVRational::operator/(qint64 a) const
{
    T_AVRational v_result = *this;
    v_result.den *= a;
    return v_result;
}
T_AVRational& T_AVRational::operator/=(qint64 a)
{
    this->den *= a;
    return *this;
}
qint64 T_AVRational::toLongLong() const
{
    return this->num / this->den;
}
double T_AVRational::toDouble() const
{
    return ((double)this->num) / this->den;
}
